﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CustomSerializer.Tests
{
    public class ObjectCollections : IComparable
    {
        public Dictionary<string, SimpleTypes> DictSimpleTypes;
        public Dictionary<string, object> Dict;
        public Dictionary<object, object> DictObj;

        public List<SimpleTypes> ListSimpleTypes;
        public List<SimpleTypes[]> ListSimpleTypesArrays;
        public List<object> ListObjects;

        public SimpleTypes[] ArraySimpleTypes;
        public SimpleTypesNullable[] ArraySimpleNullableTypes;
        public object[] ArrayObjects;

        public IList<string> IListString;
        public IList<object> IListObject;
        public ICollection<Guid> ICollectionGuid;

        public ObjectCollections() { }

        public ObjectCollections(bool init)
        {
            if (!init) return;
            Random r = new Random((int)(DateTime.Now.Ticks % int.MaxValue));

            DictSimpleTypes = new Dictionary<string, SimpleTypes>()
            {
                {"SimpleTypes", new SimpleTypes(true)},
                {"SimpleTypes2", new SimpleTypes(true)},
                {"SimpleTypes3", new SimpleTypes()},
                {"SimpleTypesNULL", null},
                {"SimpleTypes5", new SimpleTypes(true)},
                {"SimpleTypesNULLL", null},
            };

            Dict = new Dictionary<string, object>()
            {
                {"SimpleTypes", new SimpleTypes(true)},
                {"SimpleTypes2", new SimpleTypesNullable(true)},
                {"SimpleTypes3", new SimpleTypesNullableArrays(true)},
                {"SimpleTypesNULL", null},
                {"SimpleTypes5", new SimpleTypes(true)},
                {"SimpleTypesNULLL", null},
                {"SimpleTypes6", r.Next(100000)},
                {"SimpleTypes7", new int?()},
                {"SimpleTypes8", "TestValue"},
            };
            
            DictObj = new Dictionary<object, object>()
            {
                {"SimpleTypes", new SimpleTypes(true)},
                {2, new SimpleTypesNullable(true)},
                {TimeSpan.FromDays(r.Next(7)), new SimpleTypesNullableArrays(true)},
                {new DateTime(2015, 1, 1, 1, 1, 1).AddDays(r.Next(31)), null},
                {'Ы', new SimpleTypes(true)},
                {new Guid(Enumerable.Range(0, 16).Select(p => (byte)((r.Next(255) * p) & 0xFF)).ToArray()), null},
                {(double) r.Next(100000), r.Next(100000)},
                {((Int64) r.Next(int.MaxValue))*r.Next(int.MaxValue), "TestValue"},
            };
        }

        int IComparable.CompareTo(object obj) {
            var r = (ObjectCollections) obj;

            int errors = 0;
            var eq = new ObjectCollectionEqualityComparer();
            if (!((DictSimpleTypes == null && r.DictSimpleTypes == null)
                  || (DictSimpleTypes.Keys.SequenceEqual(r.DictSimpleTypes.Keys, eq) && DictSimpleTypes.Keys.All(p => compare(r.DictSimpleTypes[p], DictSimpleTypes[p]) == 0)))) errors++;

            if (!((Dict == null && r.Dict == null) || (Dict.Keys.SequenceEqual(r.Dict.Keys, eq) && Dict.Keys.All(p => compare(r.Dict[p], Dict[p]) == 0)))) errors++;
            if (!((DictObj == null && r.DictObj == null) || (DictObj.Keys.SequenceEqual(r.DictObj.Keys, eq) && DictObj.Keys.All(p => compare(r.DictObj[p], DictObj[p]) == 0)))) errors++;

            if (!((ListSimpleTypes == null && r.ListSimpleTypes == null) || ListSimpleTypes.SequenceEqual(r.ListSimpleTypes, eq))) errors++;
            if (!((ListSimpleTypesArrays == null && r.ListSimpleTypesArrays == null) || ListSimpleTypesArrays.SequenceEqual(r.ListSimpleTypesArrays, eq))) errors++;
            if (!((ListObjects == null && r.ListObjects == null) || ListObjects.SequenceEqual(r.ListObjects, eq))) errors++;

            if (!((ArraySimpleTypes == null && r.ArraySimpleTypes == null) || ArraySimpleTypes.SequenceEqual(r.ArraySimpleTypes, eq))) errors++;
            if (!((ArraySimpleNullableTypes == null && r.ArraySimpleNullableTypes == null) || ArraySimpleNullableTypes.SequenceEqual(r.ArraySimpleNullableTypes, eq))) errors++;
            if (!((ArrayObjects == null && r.ArrayObjects == null) || ArrayObjects.SequenceEqual(r.ArrayObjects, eq))) errors++;

            if (!((IListString == null && r.IListString == null) || IListString.SequenceEqual(r.IListString, eq))) errors++;
            if (!((IListObject == null && r.IListObject == null) || IListObject.SequenceEqual(r.IListObject, eq))) errors++;
            if (!((ICollectionGuid == null && r.ICollectionGuid == null) || compare(ICollectionGuid, r.ICollectionGuid) == 0)) errors++;

            return errors > 0 ? 1 : 0;
        }

        internal static int compare(object o1, object o2) {
            if (o1 == null && o2 == null) return 0;
            if (o1.GetType() != o2.GetType()) return 1;
            if (o1 is string) return string.Compare(o1 as string, o2 as string, false);
            if (o1 is IComparable) return (o1 as IComparable).CompareTo(o2);
            if (o1 is Array) {
                var a1 = (Array) o1;
                var a2 = (Array) o2;
                if (a1.Length != a2.Length) return 1;
                for (var i = 0; i<a1.Length; i++)
                    if (compare(a1.GetValue(i), a2.GetValue(i)) != 0) return 1;
                return 0;
            }

            if (o1 is ICollection) {
                var a1 = o1 as ICollection;
                var a2 = o2 as ICollection;
                if (a1.Count != a2.Count) return 1;

                var e1 = a1.GetEnumerator();
                var e2 = a2.GetEnumerator();
                e1.Reset();
                e2.Reset();
                while (e1.MoveNext())
                    if (compare(e1.Current, e2.MoveNext()) != 0) return 1;
            }
            if (o1.GetType() == typeof (object) && o2.GetType() == typeof (object)) return 0;
            return o1.Equals(o2) ? 0 : 1;
        }
    }

    class ObjectCollectionEqualityComparer : IEqualityComparer<object> {

        bool IEqualityComparer<object>.Equals(object x, object y) {
            return ObjectCollections.compare(x, y) == 0;
        }

        int IEqualityComparer<object>.GetHashCode(object obj) {
            return obj.GetHashCode();
        }
    }
}